#include "reciever.h"
#include "packet.h"
#include <time.h>
#include <sys/types.h>

// Event Types
#define TIMEOUT 		1
#define RDT_SEND 		2
#define RDT_RCV			4
//
//Packet buffers
Packet dataPacket;
CPacket controlPacket;

extern int DEBUG_ON;	// print debug messages

// reciever			
int main( int argc, char **argv )
{
	int min=0,sec =0 ,msec =0;
	struct sockaddr_in serv_addr;
	struct sockaddr_in clnt_addr;
	int serv_sock;
	int clnt_addr_size;
	int rc;
	int result = 0;
	uint16_t check;
	unsigned char seq = 1;
	int n=0;
	struct timeval ti1,ti2;
	int pack_count = 0 ,same_pack = 0 ,corr_pack = 0;
	
	DEBUG_ON = 1; 	// print debug message

	if(argc != 2){
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}
	if ((serv_sock = socket( AF_INET, SOCK_DGRAM, 0 )) < 0)
		error(1, errno, "socket creation failed" );
	
	memset(&serv_addr, 0 , sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(atoi(argv[1]));	
	
	if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
		error(1, errno, "socket bind failed");
	gettimeofday(&ti1,NULL);
	while(1) {
		memset(&dataPacket,0,sizeof(dataPacket));
		clnt_addr_size = sizeof(clnt_addr);	
		rc = recvfrom(serv_sock, &dataPacket, sizeof(dataPacket), 0, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
//		debug("=========================================\n");
//		debug("Data ==>type:%d   seq:%d   len:%x\n", dataPacket.header.type, dataPacket.header.seq, dataPacket.header.len);
		
		pack_count ++;

		if(rc > 0){
			
			if(dataPacket.header.type == DATA){
				check = dataPacket.header.checksum;
				dataPacket.header.checksum = 0;
				dataPacket.header.checksum = in_cksum((uint16_t *)&dataPacket , sizeof(dataPacket));
//                		debug("check sum : %x %x\n", dataPacket.header.checksum, check);
				if(dataPacket.header.checksum == check)
				{	
					connect(serv_sock, (struct sockaddr *)&clnt_addr,clnt_addr_size);
					make_pkt(ACK, dataPacket.header.seq, NULL,0);
					controlPacket.header.checksum = in_cksum((uint16_t *)&controlPacket, sizeof(controlPacket));
					result = udt_send(serv_sock, &controlPacket, sizeof(controlPacket));
					if(result < 0){
						printf("error acksend!\n");	
					}
				//	printf("result : %s\n",dataPacket.data);
                
//					debug("Ack ==>type:%d   seq:%d   len: %x   checksum: %x \n", controlPacket.header.type, controlPacket.header.seq, controlPacket.header.len, controlPacket.header.checksum);
                    
					if(seq != dataPacket.header.seq)
					{
						corr_pack ++;
						seq = dataPacket.header.seq;
//						debug("data : %s ,,,\n",dataPacket.data);
						dataPacket.data[strlen(dataPacket.data)]= 0;
					//	write(1,dataPacket.data,dataPacket.header.len);
//						debug("datapacket.header.len = %d\n",dataPacket.header.len);
						write(1,dataPacket.data,dataPacket.header.len-8);
						n = n+dataPacket.header.len-8;
//						debug("\n\nn = %d\n\n",n);
					}
					else{
						same_pack ++;
					}
					
//					debug("========================================\n");
				}
			}
			else if(dataPacket.header.type == END){
				
				check = dataPacket.header.checksum;
				dataPacket.header.checksum = 0;
				dataPacket.header.checksum = in_cksum((uint16_t *)&dataPacket , sizeof(dataPacket));
//				debug("End packet checksum : %x %x\n", dataPacket.header.checksum, check);
				if(check ==dataPacket.header.checksum){
					connect(serv_sock, (struct sockaddr *)&clnt_addr,clnt_addr_size);
					make_pkt(ACK, dataPacket.header.seq, NULL,0);
//					debug("End ack ==>type :%d   seq:%d   lend:%x   checksum:%x \n", controlPacket.header.type, controlPacket.header.seq, controlPacket.header.len, controlPacket.header.checksum);
                    gettimeofday(&ti2,NULL);           	
					debug("===============================================================\n");
					debug("%d of DATA packets received (including retransmitted packets)\n",pack_count);
					debug("%d of duplicated DATA packets received\n",same_pack);
					debug("%d of DATA packets correctly received\n",corr_pack);
					debug("%d of bytes correctly received\n",n);
					
					min = (ti2.tv_sec - ti1.tv_sec)/60;
					sec = (ti2.tv_sec - ti1.tv_sec)%60;
					msec = ti2.tv_usec - ti1.tv_usec;

					debug("Time duration : %d min %d sec %d msec\n",min,sec,msec);
					debug("Troughput : %d (byte/second)\n",n/(ti2.tv_sec - ti1.tv_sec));
					debug("===============================================================\n");

					result = udt_send(serv_sock, &controlPacket, sizeof(controlPacket));
					return 0;
				}
			}
		}	
		if(rc < 0)
			error(1, errno, "recvfrom failed");
	}	
}


void make_pkt(unsigned char type, unsigned char seq, char* data, unsigned short checksum){
    if(data==NULL){
        memset(&controlPacket,0,sizeof(controlPacket));
        controlPacket.header.seq = seq;
        controlPacket.header.len = HEADER_LEN;
        controlPacket.header.checksum = checksum;
        controlPacket.header.type = type;
    }

    else{
        memset(&dataPacket,0,sizeof(dataPacket));
        dataPacket.header.seq = seq;
        dataPacket.header.len = HEADER_LEN + sizeof(data);
        dataPacket.header.checksum = checksum;
        dataPacket.header.type = type;
        memcpy(&dataPacket.data,data,strlen(data));
    }
}
